"""
author:elysiane
Last_time:2024.2.17/0.21
"""
import os

from flask import Flask, request, jsonify,url_for
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_cors import CORS
from werkzeug.utils import secure_filename

app = Flask(__name__)

UPLOAD_FOLDER = 'uploads/'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} # 图片格式限制
MAX_CONTENT_LENGTH = 16 * 1024 * 1024  # 16MB 图片大小限制

# 创建SQLite数据库的URI
app.config['SQLALCHEMY_DATABASE_URI'] = ('sqlite:///software'
                                         '.db')
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = MAX_CONTENT_LENGTH
app.add_url_rule('/uploads/<filename>', 'uploaded_file', build_only=True)


db = SQLAlchemy(app)
CORS(app)

# 创建数据库迁移对象
migrate = Migrate(app, db)

# Model设置
class SoftwareType(db.Model):
    # 软件类型表
    id = db.Column(db.Integer, primary_key=True)
    category = db.Column(db.String(50), nullable=False)
    subcategory = db.Column(db.String(50), nullable=False)
    software_info = db.relationship('SoftwareInfo', backref='software_type', lazy=True)

    def __init__(self, category, subcategory):
        self.category = category
        self.subcategory = subcategory


class SoftwareInfo(db.Model):
    # 软件信息表
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    tutorial = db.Column(db.Text, nullable=False)
    download_link = db.Column(db.String(200), nullable=False)
    type_id = db.Column(db.Integer, db.ForeignKey('software_type.id'), nullable=False)

    def __init__(self, name, tutorial, download_link, type_id):
        self.name = name
        self.tutorial = tutorial
        self.download_link = download_link
        self.type_id = type_id # type_id关联两个表格


class TypeCategory(db.Model):
    # 软件大类型
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), unique=True, nullable=False)
    subtypes = db.relationship('TypeSubcategory', backref='category', lazy=True)

class TypeSubcategory(db.Model):
    # 大类型之下的小类型
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)
    category_id = db.Column(db.Integer, db.ForeignKey('type_category.id'), nullable=False)




# 路由配置

@app.route('/index', methods=['GET'])
def hello_world():
    # 测试路由
    return jsonify('Hello World')



# 对大类型进行操作的路由
from flask import request, jsonify

@app.route('/api/categories/all', methods=['POST'])
def get_all_categories_subtypes_software():
    categories = TypeCategory.query.all()
    categories_info = []

    for category in categories:
        category_data = {
            "id": category.id,
            "category": category.name,
            "subtypes": []
        }

        subtypes = TypeSubcategory.query.filter_by(category_id=category.id).all()

        for subtype in subtypes:
            subtype_data = {
                "id": subtype.id,
                "subcategory": subtype.name
            }

            category_data["subtypes"].append(subtype_data)

        categories_info.append(category_data)

    return jsonify(categories_info), 200


@app.route('/api/categories', methods=['POST'])
def add_category():
    data = request.json
    category_name = data.get('name')

    if not category_name:
        return jsonify({"message": "大类型名称不能为空"}), 400

    existing_category = TypeCategory.query.filter_by(name=category_name).first()

    if existing_category:
        return jsonify({"message": "该大类型已存在"}), 400

    new_category = TypeCategory(name=category_name)
    db.session.add(new_category)
    db.session.commit()

    return jsonify({"message": "成功添加该大类型"}), 200

@app.route('/api/categories/<int:category_id>', methods=['DELETE'])
def delete_category(category_id):
    # 删除软件大类型，注，删除该大类型会导致其下小类型一起删除
    category = TypeCategory.query.get(category_id)
    if category:
        db.session.delete(category)
        db.session.commit()
        return jsonify({"message": "该大类型已被成功删除"})
    return jsonify({"message": "对应大类型不存在"}), 404

# 小类型路由
@app.route('/api/categories/<int:category_id>/subtypes', methods=['GET'])
def get_subtypes(category_id):
    # 根据大类型id获得其下对应所有小类型
    category = TypeCategory.query.get(category_id)
    if category:
        subtypes = [{"id": subtype.id, "name": subtype.name} for subtype in category.subtypes]
        return jsonify(subtypes)
    return jsonify({"message": "对应大类型不存在"}), 404


@app.route('/api/categories/<int:category_id>/subtypes', methods=['POST'])
def add_subtype(category_id):
    # 根据大类型id添加小类型
    data = request.json
    subtype_name = data.get('name')

    if not subtype_name:
        return jsonify({"message": "小类型名称不能为空"}), 400

    category = TypeCategory.query.get(category_id)

    if category:
        existing_subtype = TypeSubcategory.query.filter_by(name=subtype_name, category_id=category_id).first()

        if existing_subtype:
            return jsonify({"message": "该小类型已存在"}), 400

        new_subtype = TypeSubcategory(name=subtype_name, category_id=category_id)
        db.session.add(new_subtype)
        db.session.commit()
        return jsonify({"message": "成功添加该小类型"}), 200

    return jsonify({"message": "对应大类型不存在"}), 404

@app.route('/api/subtypes/<int:subtype_id>', methods=['DELETE'])
def delete_subtype(subtype_id):
    # 直接删除小类型，会一并从大类型中去除
    subtype = TypeSubcategory.query.get(subtype_id)
    if subtype:
        db.session.delete(subtype)
        db.session.commit()
        return jsonify({"message": "该小类型已被删除"})
    return jsonify({"message": "对应小类型不存在"}), 404


@app.route('/api/software', methods=['POST'])
def create_software():
    # 创建新的软件信息
    data = request.json
    required_fields = ['category', 'subcategory', 'name', 'tutorial', 'download_link']

    if not all(field in data for field in required_fields):
        # 检查传参是否完整，返回错误信息
        return jsonify({"message": "参数不完整"}), 400

    category = data['category']
    subcategory = data['subcategory']

    # 检查传入的 category 和 subcategory 是否存在，并且 subcategory 是 category 的子目录
    category_exists = TypeCategory.query.filter_by(name=category).first()

    if category_exists:
        subcategory_exists = TypeSubcategory.query.filter_by(name=subcategory, category_id=category_exists.id).first()

        if subcategory_exists:
            try:
                # 创建一个新的软件类型对象
                software_type = SoftwareType(category=category, subcategory=subcategory)
                db.session.add(software_type)
                db.session.commit()

                # 创建一个新的软件信息对象
                software_info = SoftwareInfo(
                    name=data['name'],
                    tutorial=data['tutorial'],
                    download_link=data['download_link'],
                    type_id=software_type.id
                )
                db.session.add(software_info)
                db.session.commit()

                return jsonify({"message": "软件信息创建成功！"})
            except Exception as e:
                db.session.rollback()
                print(e)
                return jsonify({"message": "创建软件信息失败"}), 500
        else:
            return jsonify({"message": "提供的 subcategory 不属于 category 的子目录"}), 400
    else:
        return jsonify({"message": "提供的 category 不存在"}), 400


@app.route('/api/software/<software_id>', methods=['GET'])
def get_software(software_id):
    # 根据软件id获取软件信息
    software_info = SoftwareInfo.query.get(software_id)
    if software_info:
        # 获取软件类型信息
        software_type = SoftwareType.query.get(software_info.type_id)
        return jsonify({
            "name": software_info.name,
            "tutorial": software_info.tutorial,
            "download_link": software_info.download_link,
            "category": software_type.category,
            "subcategory": software_type.subcategory
        })
    else:
        return jsonify({"message": "该软件不存在"}), 404


@app.route('/api/search_software_name', methods=['GET'])
def search_software_name():
    search_keyword = request.args.get('keyword')

    software_info = SoftwareInfo.query.filter(SoftwareInfo.name.ilike(f"%{search_keyword}%")).all()

    if not software_info:
        return jsonify({"message": "未搜索到含有该关键词的文件."})

    software_list = []
    for software in software_info:
        software_list.append({
            'id': software.id,
            'name': software.name,
            'tutorial': software.tutorial,
            'download_link': software.download_link
        })

    return jsonify({
        'software_info': software_list
    })


@app.route('/api/software/<software_id>', methods=['PUT'])
def update_software(software_id):
    # 根据软件id更新信息，只写了更改名字，安装教程，下载链接
    software_info = SoftwareInfo.query.get(software_id)
    if software_info:
        data = request.json
        software_info.name = data['name']
        software_info.tutorial = data['tutorial']
        software_info.download_link = data['download_link']

        db.session.commit()

        return jsonify({"message": "软件信息更新成功"})
    else:
        return jsonify({"message": "软件不存在"}), 404


@app.route('/api/software/type/<software_id>', methods=['PUT'])
def update_software_type(software_id):
    # 根据软件id更新类型信息
    software_info = SoftwareInfo.query.get(software_id)
    if software_info:
        data = request.json
        category = data.get('category')
        subcategory = data.get('subcategory')

        if category and subcategory:
            software_info.software_type.category = category
            software_info.software_type.subcategory = subcategory

            db.session.commit()

            return jsonify({"message": "软件类型信息更新成功"})
        else:
            return jsonify({"message": "请提供有效的分类和子分类信息"}), 400
    else:
        return jsonify({"message": "软件不存在"}), 404


@app.route('/api/software/<software_id>', methods=['DELETE'])
def delete_software(software_id):
    # 根据id删除软件
    software_info = SoftwareInfo.query.get(software_id)
    if software_info:
        db.session.delete(software_info)
        db.session.commit()
        return jsonify({"message": "软件删除成功"})
    else:
        return jsonify({"message": "软件不存在"}), 404


# 前端搜索部分
@app.route('/api/software/search_by_category', methods=['POST'])
def search_software_by_category_and_keyword():
    data = request.get_json()
    search_keyword = data.get('search_keyword')
    category = data.get('category')

    if search_keyword and category:
        software_info_list = []

        subtypes = TypeSubcategory.query.filter_by(category_id=category).all()

        for subtype in subtypes:
            software_list = SoftwareInfo.query.filter(SoftwareInfo.type_id == subtype.id, SoftwareInfo.name.like(f"%{search_keyword}%")).all()

            for software in software_list:
                software_info = {
                    "id": software.id,
                    "name": software.name,
                    "tutorial": software.tutorial,
                    "download_link": software.download_link
                }
                software_info_list.append(software_info)

        return jsonify(software_info_list)
    else:
        return jsonify({"message": "未提供查询关键字或大类型"}), 400




# 后端搜索部分
@app.route('/api/search_software', methods=['GET'])
def search_software():
    search_keyword = request.args.get('keyword')

    software_info = SoftwareInfo.query.filter(SoftwareInfo.name.ilike(f"%{search_keyword}%")).all()

    if not software_info:
        return jsonify({"message": "未搜索到含有该关键词的文件."})

    total_count = len(software_info)

    software_list = []
    for software in software_info:
        software_list.append({
            'id': software.id,
            'name': software.name,
            'tutorial': software.tutorial,
            'download_link': software.download_link
        })

    return jsonify({
        'total_count': total_count,
        'software_info': software_list
    })


@app.route('/api/all_software', methods=['GET'])
def get_all_software():
    software_info = SoftwareInfo.query.all()

    software_list = []
    for software in software_info:
        software_list.append({
            'name': software.name,
            'tutorial': software.tutorial,
            'download_link': software.download_link,
            'id':software.id
        })

    return jsonify({"software_info": software_list})


# 文件上传部分
def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/api/software/upload_image', methods=['POST'])
def upload_image():
    if 'file' not in request.files:
        return jsonify({'error': '无文件部分'}), 400

    file = request.files['file']
    if file.filename == '':
        return jsonify({'error': '没有选中文件'}), 400

    category = request.form.get('category')
    subcategory = request.form.get('subcategory')
    software_name = request.form.get('name')

    if not category or not subcategory or not software_name:
        return jsonify({'error': '缺少所需的软件信息参数'}), 400

    # 构建文件存储路径
    folder_path = os.path.join(app.config['UPLOAD_FOLDER'], f"{category}_{subcategory}_{software_name}")
    if not os.path.exists(folder_path):
        os.makedirs(folder_path)

    filename = secure_filename(file.filename)
    file.save(os.path.join(folder_path, filename))

    # 构建返回的 URL
    file_url = url_for('uploaded_file', filename=f"{category}_{subcategory}_{software_name}/{filename}")

    return jsonify({'message': '文件已成功上传', 'file_url': file_url}), 200



# 图片访问部分
from flask import send_from_directory
from urllib.parse import unquote

@app.route('/uploads/<path:filename>')
def uploaded_file(filename):
    filename = unquote(filename)  # 解码中文字符
    return send_from_directory(app.config['UPLOAD_FOLDER'], filename)



if __name__ == '__main__':
    print("启动成功")
    # 创建所有数据库表
    with app.app_context():
        db.create_all()
    # 启动
    app.run(host="0.0.0.0")